<?php

require_once 'Pipes/Database/Sequence.php';
require_once 'Pipes/FileSystem/Model/Finder.php';
require_once 'Pipes/FileSystem/Model/Loader.php';
require_once 'Pipes/FileSystem/ModelMeta/Finder.php';
require_once 'Pipes/FileSystem/ModelMeta/Loader.php';
require_once 'Pipes/Model.php';
require_once 'Pipes/Model/Visitors/ExternalRelationshipSaver.php';
require_once 'Pipes/Model/Visitors/ExternalRelationshipRemover.php';
require_once 'Pipes/Namespace/Parser.php';
require_once 'Pipes/Storage.php';
require_once 'PipesImpl/Storage.php';
require_once 'PipesImpl/Model/Mapper.php';
require_once 'PipesImpl/Config.php';
require_once 'Pipes/ModelList.php';
require_once 'Pipes/Filter.php';

class Pipes_Model_Mapper implements PipesImpl_Model_Mapper
{
    protected $_storage = null;
    protected $_config = null;
    protected $_project = '';
    protected $_name = '';
    protected $_modelLoader = null;
    protected $_metaLoader = null;
    
    public function __construct(PipesImpl_Config $config, PipesImpl_Storage $storage) {
        $this->_storage = $storage;
        $this->_config = $config;
        
        $this->_project = $config->namespace;
        
        $exploded = explode('_', get_class($this));
        $this->_name = substr(array_pop($exploded), 0, -6);
        $this->_modelLoader = new Pipes_FileSystem_Model_Loader(
            new Pipes_FileSystem_Model_Finder($config)
        );
        $this->_metaLoader = new Pipes_FileSystem_ModelMeta_Loader(
            new Pipes_FileSystem_ModelMeta_Finder($config)
        );
    }
    
    public function find($criteria = null) {
        $this->_modelLoader->load($this->_name);
        $modelName = $this->_modelLoader->lastLoaded;
        
        // load meta
        $this->_metaLoader->load($this->_name);
        $metaName = $this->_metaLoader->lastLoaded;
        $meta = new $metaName($this->_config);
        
        if (is_null($criteria)) {
            $properties = array();
        } else {
            if (!is_object($criteria) && !empty($criteria)) {
                $idValue = $criteria;
                $criteria = new Pipes_Filter();
                $keyColumn = $this->_name . '_id';
                $criteria->$keyColumn->equals($idValue);
            }
            $properties = $this->_storage->getRecord($this->_name, $criteria);
        }
        
        $model = new $modelName($meta, $properties);
        $model->attach(array($this, 'save'));
        return $model;
    }
    
    public function findAll()
    {
        $this->_modelLoader->load($this->_name . 'List');
        $modelListName = $this->_modelLoader->lastLoaded;
        
        // load meta
        $this->_metaLoader->load($this->_name);
        $metaName = $this->_metaLoader->lastLoaded;
        $meta = new $metaName($this->_config);
        
        $recordSet = $this->_storage->getRecords($this->_name);
        return new $modelListName($this->_config, $meta, $recordSet);
    }
    
    public function save(PipesImpl_Model $model) 
    {
        $model->accept(new Pipes_Model_Visitors_ExternalRelationshipSavers($this->_config));

        if ($model->isNew()) {
            $modelName = $model->getName();
            $idKey = $modelName . '_id';
            if (!isset($model->$idKey)) {
                $sequence = new Pipes_Database_Sequence($this->_config);
                $model->$idKey = $sequence->next();
            }
            $this->_storage->add($modelName, $model->toArray());
        } else {
            $this->_storage->modify($model->getName(), $model->toArray());
        }
    }

    public function remove(PipesImpl_Model $model) {
        $model->accept(new Pipes_Model_Visitors_ExternalRelationshipRemover($this->_config));

        $id = $model->idColumn()->name;
        $this->_storage->remove($model->getName(), $model->$id);
    }
}

